﻿#include "timerwheel.h"
#include <sys/timerfd.h>
#include <unistd.h>
#include "eventloop.h"
#include "channel.h"
#include "log.h"


// TimerTask
TimerTask::TimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &cb)
    : m_id(id)
    , m_timeout(timeout)
    , m_taskCb(cb)
    , m_isCanceled(false)
{
}
// 析构时，执行任务，并释放在时间论的信息(id关联的指针)
TimerTask::~TimerTask()
{
    if (m_isCanceled == false)
    {
        m_taskCb();
    }
    m_realse();
}
void TimerTask::setRealse(const RealseTaskFunc_t &rcb)
{
    m_realse = rcb;
}

uint32_t TimerTask::getTimeout()
{
    return m_timeout;
}

void TimerTask::cancel()
{
    m_isCanceled = true;
}

// TimerWheel
int TimerWheel::CreateTimerfd()
{
    int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (tfd < 0)
    {
        LOG(Fatal, "TIMERFD CREATE ERROR");
        abort();
    }
    // int timerfd_settime(int fd, int flags, struct itimerspec *new, struct itimerspec *old);
    struct itimerspec itime;
    itime.it_value.tv_sec = 1;
    itime.it_value.tv_nsec = 0; // 第一次超时时间为1s后
    itime.it_interval.tv_sec = 1;
    itime.it_interval.tv_nsec = 0; // 第一次超时后，每次超时的间隔时
    timerfd_settime(tfd, 0, &itime, NULL);
    return tfd;
}

TimerWheel::TimerWheel(EventLoop *eloop)
    : m_eloop(eloop)
    , m_tick(0)
    , m_maxcap(60)
    , m_wheel(m_maxcap)
    , m_infos()
    , m_timerfd(CreateTimerfd())
    , m_timerfd_channel(new Channel(eloop, m_timerfd))
{
    m_timerfd_channel->setReadCallback(std::bind(&TimerWheel::start, this));
    m_timerfd_channel->enableRead();
}

TimerWheel::~TimerWheel()
{

}

void TimerWheel::removeInfo(uint64_t id)
{
    auto it = m_infos.find(id);
    if(it == m_infos.end())
    {
        LOG(Warning, "removeInfo: TASK NOT FOUND");
        return;
    }
    m_infos.erase(it);
}

void TimerWheel::_addTimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &task)
{
    if(timeout < 0)
    {
        LOG(Warning, "TIMEOUT ERROR");
        return;
    }
    // 1. 创建任务
    StaskPtr taskSPtr(new TimerTask(id, timeout, task));
    taskSPtr->setRealse(std::bind(&TimerWheel::removeInfo, this, id));
    // 2. 添加任务
    // NOTE: 不保留任务的所有权，因此使用weak_ptr
    m_infos[id] = WtaskPtr(taskSPtr);
    // 3. 计算任务在时间轮中的位置
    int pos = (m_tick + timeout) % m_maxcap;
    m_wheel[pos].push_back(taskSPtr);
}

void TimerWheel::_refreshTimerTask(uint64_t id)
{
    auto it = m_infos.find(id);
    if(it == m_infos.end())
    {
        LOG(Warning, "_refreshTimerTask: TASK NOT FOUND");
        return;
    }
    // 1. 获取任务
    // NOTE: 由设计思路：id存在，则一定存在不为nullptr是shared_ptr
    auto taskSPtr = it->second.lock();
    // 2. 刷新任务
    int timeout = taskSPtr->getTimeout();
    int pos = (m_tick + timeout) % m_maxcap;
    m_wheel[pos].push_back(taskSPtr);
}

void TimerWheel::_cancelTimerTask(uint64_t id)
{
    auto it = m_infos.find(id);
    if(it == m_infos.end())
    {
        LOG(Warning, "_cancelTimerTask: TASK NOT FOUND");
        return;
    }
    // 1. 获取任务
    auto taskSPtr = it->second.lock();
    // 2. 取消任务
    // NOTE: 如果任务指针为空，则说明任务已经被释放，不需要取消
    if(taskSPtr)
    {
        taskSPtr->cancel();
    }
}

void TimerWheel::addTimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &cb)
{
    m_eloop->runInLoop(std::bind(&TimerWheel::_addTimerTask, this, id, timeout, cb));
}
void TimerWheel::refreshTimerTask(uint64_t id)
{
    m_eloop->runInLoop(std::bind(&TimerWheel::_refreshTimerTask, this, id));
}
void TimerWheel::cancelTimerTask(uint64_t id)
{
    m_eloop->runInLoop(std::bind(&TimerWheel::_cancelTimerTask, this, id));
}

// 
bool TimerWheel::hasTimerTask(uint64_t id)
{
    auto it = m_infos.find(id);
    if(it == m_infos.end())
    {
        return false;
    }
    return true;
}

int TimerWheel::readTimerFd()
{
    uint64_t times;
    // NOTE: times不一定为1，因为在处理某个事件可能花费时间，导致超时次数大于1
    ssize_t n = ::read(m_timerfd, &times, sizeof(times));
    if(n < 0)
    {
        LOG(Warning, "TIMERFD READ ERROR");
        return 0;
    }
    return times;
}

void TimerWheel::goTick()
{
    // 单次转动时间轮
    m_tick = (m_tick + 1) % m_maxcap;
    // 清除时间轮中到期的任务
    m_wheel[m_tick].clear();
}

void TimerWheel::start()
{
    // 1. 获取超时次数
    uint64_t times = readTimerFd();
    for(int i = 0; i < times; i++)
    {
        // 2. 转动时间轮
        goTick();
    }
}